package org.jlambda.util;

import java.util.Iterator;

import org.jlambda.functions.Fun0;
import org.jlambda.functions.Fun1;
import org.jlambda.functions.Fun2;

final public class Logic {
	
	/**
	 * Returns the return of the function, ignores the second argument
	 * 
	 * value f object
	 */
	public static class Value<T> extends Fun2<Fun0<T>, T,T> {
		@Override
		public T apply(Fun0<T> f, T p2) {
			return f.apply();
		}
	};
	
	/**
	 * Returns f
	 * 
	 * echo f
	 */
	public static class Echo<T> extends Fun1<T,T> {
		@Override
		public T apply(T p2) {
			return p2;
		}
	};
	
	/**
	 * Returns the result of calling the Deferred function with the Object
	 * 
	 * eval Function Object
	 */
	public static class Eval<T,R> extends Fun2<Fun1<T,R>, T, R> {
		@Override
		public R apply(Fun1<T, R> p1, T p2) {
			return p1.apply(p2);
		}
	};
	
	/**
	 * Returns true if the .equals method returns true of Object 1 on Object 2.
	 * 
	 * if the first is null, it returns false
	 * 
	 * equal Object Object
	 */
	public static final class Equal<T> extends Fun2<T, T, Boolean> {
		@Override
		public Boolean apply(T p1, T p2) {
			if (p1 == null) {
				return false;
			}
			return p1.equals(p2);
		}
	};
	
	public static final <T> Fun1<T, Boolean> eq(T p1){
		return new Equal<T>().apply(p1);
	}
	
	public static final Fun2<Object, Object, Boolean> equal = new Equal<Object>();
	
	/**
	 * Returns true if all elements in the list are true
	 * 
	 * and LIST<Boolean>
	 */
	public static Fun1<Iterable<Boolean>, Boolean > and = new Fun1<Iterable<Boolean>, Boolean >() {
		@Override
		public Boolean apply(Iterable<Boolean> p1) {
			Iterator<Boolean> itr = p1.iterator();
			
			while (itr.hasNext()) {
				Boolean element = itr.next();
				if (element==null) {
					return false;
				}
				if (!element.booleanValue()) {
					return false;
				}
			}
			return true;
		}
	};
	
	/**
	 * Returns true the condition is false
	 * 
	 * not Boolean
	 */
	public static Fun1<Boolean, Boolean > not = new Fun1<Boolean, Boolean >() {
		@Override
		public Boolean apply(Boolean l) {
			if (l == null ) {
				return null;
			}
			if (l.equals(false)){
				return true;
			}
			
			return false;
		}
	};
	
	public static class NotF<T> extends Fun2<Fun1<T,Boolean>, T, Boolean >{
		@Override
		public Boolean apply(Fun1<T,Boolean> f, T val) {
			if (f == null) {
				return null;
			}
			Boolean l = f.apply(val);
			
			if (l.equals(false)){
				return true;
			}
			
			return false;
		}
	};
	
	/**
	 * Returns true the condition is true
	 * 
	 * is Boolean
	 */
	public static Fun1<Boolean, Boolean > is = new Fun1<Boolean, Boolean >() {
		@Override
		public Boolean apply(Boolean l) {
			if (l == null ) {
				return null;
			}
			if (l.equals(true)){
				return true;
			}
			
			return false;
		}
	};
	public static final Fun1<Boolean, Boolean > _true  = is;
	
	/**
	 * Always returns true, useful for calluntil defaults or tuple Matches
	 * 
	 */
	public static class True<T> extends Fun1<T, Boolean > {
		@Override
		public Boolean apply(T l) {
			return true;
		}
	};	
	
	/**
	 * Returns true if any of the elements in the list are true.  One condition being null but the other true will still return true.
	 * 
	 * or LIST<Boolean>
	 */
	public static Fun1<Iterable<Boolean>, Boolean > or = new Fun1<Iterable<Boolean>, Boolean >() {
		@Override
		public Boolean apply(Iterable<Boolean> p1) {
			Iterator<Boolean> itr = p1.iterator();
			
			while (itr.hasNext()) {
				Boolean element = itr.next();
				if (element!=null && element.booleanValue()) {
					return true;
				}
			}
			return false;
		}
	};
	
	
	/**
	 * Returns true if any of the elements in this list match the condition
	 * 
	 * any function LIST
	 */
	public static class Any<T> extends Fun2< Fun1<T, Boolean>, Iterable<T>, Boolean > {
		@Override
		public Boolean apply(Fun1<T, Boolean> p1, Iterable<T> p2) {
			Iterator<T> itr = p2.iterator();
			
			while (itr.hasNext()) {
				T element = itr.next();
				Boolean res = p1.apply(element);
				if (res!=null && res.booleanValue()) {
					return true;
				}
			}
			return false;
		}
	};
	public static final <T> Boolean any(Fun1<T, Boolean> p1, Iterable<T> p2) {
		return new Any<T>().apply(p1,p2);
	}
	
	/**
	 * Returns true if all of the elements in this list match the condition
	 * 
	 * all function LIST
	 */
	public static class All<T> extends Fun2< Fun1<T, Boolean>, Iterable<T>, Boolean > {
		@Override
		public Boolean apply(Fun1<T, Boolean> p1, Iterable<T> p2) {
			Iterator<T> itr = p2.iterator();
			
			while (itr.hasNext()) {
				T element = itr.next();
				Boolean res = p1.apply(element);
				if (res!=null && !res.booleanValue()) {
					return false;
				}
			}
			return true;
		}
	}
	public static final <T> Boolean all(Fun1<T, Boolean> p1, Iterable<T> p2) {
		return new All<T>().apply(p1,p2);
	}
	
	/**
	 * Returns true if all of the predicates in this list match the value
	 * 
	 * allTrue predicatelist value
	 */
	public static class AllTrue<T> extends Fun2< Iterable<Fun1<T, Boolean>>, T, Boolean > {
		@Override
		public Boolean apply(Iterable< Fun1<T,Boolean> > p1, T t) {
			Iterator<Fun1<T,Boolean>> itr = p1.iterator();
			
			while (itr.hasNext()) {
				Fun1<T,Boolean> f = itr.next();
				Boolean res = f.apply(t);
				if (res!=null && !res.booleanValue()) {
					return false;
				}
			}
			return true;
		}
	}
	public static final <T> Boolean allTrue(Iterable< Fun1<T,Boolean> > p1, T t) {
		return new AllTrue<T>().apply(p1,t);
	}
	
	/**
	 * Returns true if any of the predicates in this list match the value
	 * 
	 * anyTrue predicatelist value
	 */
	public static class AnyTrue<T> extends Fun2< Iterable<Fun1<T, Boolean>>, T, Boolean > {
		@Override
		public Boolean apply(Iterable< Fun1<T,Boolean> > p1, T t) {
			Iterator<Fun1<T,Boolean>> itr = p1.iterator();
			
			while (itr.hasNext()) {
				Fun1<T,Boolean> f = itr.next();
				Boolean res = f.apply(t);
				if (res!=null && res.booleanValue()) {
					return true;
				}
			}
			return false;
		}
	}
	public static final <T> Boolean anyTrue(Iterable< Fun1<T,Boolean> > p1, T t) {
		return new AnyTrue<T>().apply(p1,t);
	}
	
	/**
	 * Returns 1 when the argument is true, 0 if its null or false
	 */
	public final static Fun1<Boolean, Number> trueTo1 = new Fun1<Boolean, Number>(){
		@Override
		public Number apply(Boolean param) {
			if (param) return 1;
			return 0;
		}
	};
	
}
